package com.kwdb.server.storage;

import com.kwdb.grpc.DataPoint;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.List;
import java.util.Map;

/**
 * KWDB 数据库存储引擎实现
 * 使用 JDBC (PostgreSQL 协议) 连接 KWDB 数据库
 */
public class KWDBStorageEngine implements StorageEngine {
    
    private static final Logger logger = LoggerFactory.getLogger(KWDBStorageEngine.class);
    private final HikariDataSource dataSource;
    private final boolean useRealDatabase;
    
    /**
     * 构造函数 - 连接到 KWDB 数据库
     * @param host KWDB 主机地址
     * @param port KWDB 端口（默认26257）
     * @param database 数据库名
     * @param username 用户名
     * @param password 密码
     */
    public KWDBStorageEngine(String host, int port, String database, 
                             String username, String password) {
        logger.info("初始化 KWDB 存储引擎: {}:{}/{}", host, port, database);
        
        // 检查是否使用真实数据库（如果配置为空则使用模拟模式）
        this.useRealDatabase = host != null && !host.isEmpty() && !host.equals("none");
        
        if (!useRealDatabase) {
            logger.warn("未配置 KWDB 连接信息，使用模拟存储模式");
            this.dataSource = null;
            return;
        }
        
        try {
            // 配置 HikariCP 连接池
            HikariConfig config = new HikariConfig();
            
            // KWDB JDBC URL (兼容 PostgreSQL 协议)
            String jdbcUrl = String.format("jdbc:postgresql://%s:%d/%s", host, port, database);
            config.setJdbcUrl(jdbcUrl);
            config.setUsername(username);
            config.setPassword(password);
            
            // 驱动类名
            config.setDriverClassName("org.postgresql.Driver");
            
            // 连接池配置
            config.setMaximumPoolSize(20);
            config.setMinimumIdle(5);
            config.setConnectionTimeout(30000); // 30秒
            config.setIdleTimeout(600000); // 10分钟
            config.setMaxLifetime(1800000); // 30分钟
            
            // 连接测试
            config.setConnectionTestQuery("SELECT 1");
            
            // 连接池名称
            config.setPoolName("KWDB-Pool");
            
            this.dataSource = new HikariDataSource(config);
            
            // 测试连接
            testConnection();
            
            // 初始化表结构
            initTables();
            
            logger.info("KWDB 存储引擎初始化完成");
            
        } catch (Exception e) {
            logger.error("初始化 KWDB 存储引擎失败", e);
            throw new RuntimeException("无法连接到 KWDB 数据库: " + e.getMessage(), e);
        }
    }
    
    /**
     * 测试数据库连接
     */
    private void testConnection() {
        try (Connection conn = dataSource.getConnection();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT version()")) {
            if (rs.next()) {
                String version = rs.getString(1);
                logger.info("成功连接到数据库，版本: {}", version);
            }
        } catch (SQLException e) {
            logger.error("数据库连接测试失败", e);
            throw new RuntimeException("数据库连接测试失败", e);
        }
    }
    
    /**
     * 初始化数据库表
     */
    private void initTables() {
        // KWDB 时序表创建语句
        String createTableSQL = 
            "CREATE TABLE IF NOT EXISTS kwdb_measurements (" +
            "    measurement VARCHAR(255) NOT NULL," +
            "    tags TEXT," +
            "    fields TEXT," +
            "    timestamp BIGINT NOT NULL," +
            "    PRIMARY KEY (measurement, timestamp)" +
            ")";
        
        try (Connection conn = dataSource.getConnection();
             Statement stmt = conn.createStatement()) {
            stmt.execute(createTableSQL);
            logger.info("数据表初始化成功");
        } catch (SQLException e) {
            // 如果表已存在或其他非致命错误，只记录警告
            logger.warn("初始化表时出现警告（可能表已存在）: {}", e.getMessage());
        }
    }
    
    @Override
    public void write(DataPoint dataPoint) throws Exception {
        if (!useRealDatabase) {
            // 模拟模式，只记录日志
            logger.debug("[模拟] 写入数据点: {}", dataPoint.getMeasurement());
            return;
        }
        
        String sql = 
            "INSERT INTO kwdb_measurements (measurement, tags, fields, timestamp) " +
            "VALUES (?, ?, ?, ?)";
        
        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            
            pstmt.setString(1, dataPoint.getMeasurement());
            pstmt.setString(2, mapToJson(dataPoint.getTagsMap()));
            pstmt.setString(3, mapToJson(dataPoint.getFieldsMap()));
            pstmt.setLong(4, dataPoint.getTimestamp());
            
            int rows = pstmt.executeUpdate();
            
            if (rows > 0) {
                logger.debug("写入数据点成功: {}", dataPoint.getMeasurement());
            }
            
        } catch (SQLException e) {
            logger.error("写入数据失败", e);
            throw new Exception("写入 KWDB 失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public void writeBatch(List<DataPoint> dataPoints) throws Exception {
        if (!useRealDatabase) {
            // 模拟模式
            logger.debug("[模拟] 批量写入 {} 条数据", dataPoints.size());
            return;
        }
        
        if (dataPoints == null || dataPoints.isEmpty()) {
            return;
        }
        
        String sql = 
            "INSERT INTO kwdb_measurements (measurement, tags, fields, timestamp) " +
            "VALUES (?, ?, ?, ?)";
        
        Connection conn = null;
        PreparedStatement pstmt = null;
        
        try {
            conn = dataSource.getConnection();
            conn.setAutoCommit(false);
            
            pstmt = conn.prepareStatement(sql);
            
            for (DataPoint point : dataPoints) {
                pstmt.setString(1, point.getMeasurement());
                pstmt.setString(2, mapToJson(point.getTagsMap()));
                pstmt.setString(3, mapToJson(point.getFieldsMap()));
                pstmt.setLong(4, point.getTimestamp());
                pstmt.addBatch();
            }
            
            int[] results = pstmt.executeBatch();
            conn.commit();
            
            logger.info("批量写入 {} 条数据成功", results.length);
            
        } catch (SQLException e) {
            if (conn != null) {
                try {
                    conn.rollback();
                    logger.warn("批量写入失败，已回滚事务");
                } catch (SQLException rollbackEx) {
                    logger.error("回滚失败", rollbackEx);
                }
            }
            logger.error("批量写入失败", e);
            throw new Exception("批量写入 KWDB 失败: " + e.getMessage(), e);
        } finally {
            if (pstmt != null) {
                try {
                    pstmt.close();
                } catch (SQLException e) {
                    logger.error("关闭 PreparedStatement 失败", e);
                }
            }
            if (conn != null) {
                try {
                    conn.setAutoCommit(true);
                    conn.close();
                } catch (SQLException e) {
                    logger.error("关闭 Connection 失败", e);
                }
            }
        }
    }
    
    /**
     * 执行查询
     * @param sql SQL 查询语句
     * @return ResultSet
     */
    public ResultSet query(String sql) throws SQLException {
        if (!useRealDatabase) {
            throw new SQLException("模拟模式不支持查询");
        }
        
        Connection conn = dataSource.getConnection();
        Statement stmt = conn.createStatement();
        return stmt.executeQuery(sql);
    }
    
    /**
     * 获取数据源（供 QueryEngine 使用）
     */
    public HikariDataSource getDataSource() {
        return dataSource;
    }
    
    /**
     * 是否使用真实数据库
     */
    public boolean isUseRealDatabase() {
        return useRealDatabase;
    }
    
    @Override
    public void shutdown() {
        if (dataSource != null && !dataSource.isClosed()) {
            dataSource.close();
            logger.info("KWDB 存储引擎已关闭");
        }
    }
    
    /**
     * 将 Map 转为 JSON 字符串
     * 简单实现，避免依赖 Jackson
     */
    private String mapToJson(Map<String, String> map) {
        if (map == null || map.isEmpty()) {
            return "{}";
        }
        
        StringBuilder json = new StringBuilder("{");
        boolean first = true;
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (!first) {
                json.append(",");
            }
            json.append("\"").append(escapeJson(entry.getKey())).append("\":")
                .append("\"").append(escapeJson(entry.getValue())).append("\"");
            first = false;
        }
        json.append("}");
        return json.toString();
    }
    
    /**
     * 转义 JSON 字符串
     */
    private String escapeJson(String str) {
        if (str == null) {
            return "";
        }
        return str.replace("\\", "\\\\")
                  .replace("\"", "\\\"")
                  .replace("\n", "\\n")
                  .replace("\r", "\\r")
                  .replace("\t", "\\t");
    }
}

